package com.lopo.mybatis_auto_insert;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;


import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class InsertInterceptor implements Interceptor {
    private final Set<String> cache = new HashSet<>();

    private Properties properties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        final Object[] args = invocation.getArgs();
//        Executor executor = (Executor) invocation.getTarget();

        MappedStatement mappedStatement = (MappedStatement) args[0];
        Object parameter = args[1];

        //创建新的参数隐射
        if (!cache.contains(mappedStatement.getId())) {
            BoundSql boundSql = mappedStatement.getBoundSql(args[1]);
            final String sql = boundSql.getSql();
            final String newSql = updateSql(sql);
            List<ParameterMapping> newParameterMappings = new ArrayList<>();
            ParameterMapping createTimeMapping = new ParameterMapping.Builder(mappedStatement.getConfiguration(), "createTime", Object.class).build();
            newParameterMappings.add(createTimeMapping);
            newParameterMappings.addAll(mappedStatement.getSqlSource().getBoundSql(null).getParameterMappings());
            //创建新的SqlSource
            StaticSqlSource staticSqlSource = new StaticSqlSource(mappedStatement.getConfiguration(), newSql, newParameterMappings);
            Field msField = invocation.getArgs()[0].getClass().getDeclaredField("sqlSource");
            msField.setAccessible(true);
            msField.set(invocation.getArgs()[0], staticSqlSource);
            cache.add(mappedStatement.getId());
        }


        try {
            final Class<?> parameterClass = parameter.getClass();
            //获取createDate 属性描述器
            final PropertyDescriptor propertyDescriptor = new PropertyDescriptor("createTime", parameterClass);
            //获取createDate 写方法
            final Method writeMethod = propertyDescriptor.getWriteMethod();
            //调用createDate 写方法
            writeMethod.invoke(parameter, new Date());
        } catch (IntrospectionException e) {
            try {
                MapperMethod.ParamMap<Object> map = (MapperMethod.ParamMap) parameter;
                map.put("createTime", LocalDateTime.now());
            } catch (Exception exception) {
                throw new RuntimeException("use param");
            }
        }
        return invocation.proceed();
//        return executor.update(mappedStatement, parameter);
    }


    public String insertAudit(String source) {
        char[] chars = source.toCharArray();
        StringBuilder newSql = new StringBuilder();
        boolean afterFormalParam = false;
        for (char c : chars) {
            String cStr = String.valueOf(c);
            newSql.append(cStr);
            if (cStr.equals("(")) {
                if (afterFormalParam) {
                    newSql.append("?,?,?,?,?,");
                } else {
                    newSql.append("create_by,create_time,update_by,update_time,del_flag,");
                }
            }
            if (cStr.equals(")")) {
                afterFormalParam = true;
            }
        }
        return newSql.toString();
    }

    public String updateSql(String source) {
        String[] strArray = source.split("\\s+");
        StringBuilder newSql = new StringBuilder();
        boolean afterSet = false;
        for (String s : strArray) {
            if (afterSet) {
                s = "create_time=?," + s;
                afterSet = false;
            }
            if (s.equals("set") || s.equals("SET")) {
                afterSet = true;
            }
            newSql.append(s).append(" ");
        }
        return newSql.toString();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 设置变量
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}